﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace VFCons
{
    public class WndPictureS : UserControl
    {
        public delegate void DelegateDraw(WndPictureS pthis, Graphics g, float alpha, RectangleF rectImg);

        public event DelegateDraw OnDrawOnBufferedGraphics;
        public event DelegateDraw OnDrawOnPaintGraphics;
        public EventHandler OnMouseRightClicked;


        public delegate void DelegateSelect(WndPictureS pthis, PointF pt1, PointF pt2);
        public event DelegateSelect OnSelectRect;

        public delegate void DelegateMousePosition(WndPictureS pthis, PointF pt, float alpha, RectangleF rectImg);
        public DelegateMousePosition EMouseMove;

        public DelegateMousePosition EMouseClicked;

        public WndPictureS()
        {
            SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
            ClipRect = ClientRectangle;
            TextPos = new Point(50, 50);
            CanZoom = false;
        }

        public Point TextPos { get; set; }
        public bool CanZoom { get; set; }

        public Image Image
        {
            get { return _image; }
            set
            {
                if (value == null) return;
                if (CanZoom && _image == null)
                {
                    pt0 = calcCenterLT(ClipRect, value.Width, value.Height, _alpha);
                    //ptOffset = new Point(0, 0);
                    //_alpha = Math.Min(1.0f * Width / value.Width, 1.0f * Height / value.Height);
                    //pt0 = calcCenterLT(ClipRect, value.Width, value.Height, _alpha);                    
                }

                _image = value;
                Text = DateTime.Now.ToString("HH:mm:ss fff");
                if (Visible) Invalidate(); //使控件的特定区域无效并向控件发送绘制消息。
            }
        }
        private Image _image;

        private float _alpha = 1.0f;
        public float Alpha
        {
            get { return _alpha; }
            set
            {
                _alpha = value;
                Invalidate();
            }
        }

        private void drawImageInRect(Graphics gf, Rectangle clipRect)
        {
            Pen penBorder = new Pen(BackColor, 0);
            SolidBrush brushBgColor = new SolidBrush(BackColor);

            //e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
            //e.Graphics.DrawRectangle(penBorder, e.ClipRectangle);
            //e.Graphics.FillRectangle(brushBgColor, e.ClipRectangle);

            RectangleF dstRect = new RectangleF(0, 0, clipRect.Width, clipRect.Height);
            ClipRect = new Rectangle(0, 0, clipRect.Width, clipRect.Height);

            using (BufferedGraphics buffGraphics = BufferedGraphicsManager.Current.Allocate(gf, ClipRect))
            {

                Graphics g = buffGraphics.Graphics;
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
                g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
                g.DrawRectangle(penBorder, ClipRect);
                g.FillRectangle(brushBgColor, ClipRect);

                if (_image != null)
                {
                    lock (_image)
                    {
                        RectangleF dst = GetRectangle(ClipRect.Width, ClipRect.Height);
                        g.DrawImage(_image, dst, new RectangleF(0, 0, _image.Width, _image.Height), GraphicsUnit.Pixel);
                        dstRect = dst;
                    }
                }
                rectImg = dstRect;
                if (_mouseSelecting)
                {
                    if (_selectionSequareRect) drawSelectSequare(g);
                    else drawSelectRect(g);
                }

                if (OnDrawOnBufferedGraphics != null) OnDrawOnBufferedGraphics(this, g, _alpha, dstRect);

                //g.DrawString(Text, Font, new SolidBrush(ForeColor), TextPos);

                buffGraphics.Render(gf);
                g.Dispose();
            }
            if (OnDrawOnPaintGraphics != null) OnDrawOnPaintGraphics(this, gf, _alpha, ClipRect);
        }

        public void ShowImageRT(Bitmap bmp)
        {
            _image = bmp;
            using (Graphics gf = CreateGraphics())
            {
                drawImageInRect(gf, new Rectangle(0, 0, Width, Height));
            }
        }

        public Rectangle ClipRect = new Rectangle();
        private RectangleF rectImg = new RectangleF();
        protected override void OnPaint(PaintEventArgs e)
        {
            drawImageInRect(e.Graphics, e.ClipRectangle);


            //        Pen penBorder = new Pen(BackColor, 0);
            //        SolidBrush brushBgColor = new SolidBrush(BackColor);

            //        //e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
            //        //e.Graphics.DrawRectangle(penBorder, e.ClipRectangle);
            //        //e.Graphics.FillRectangle(brushBgColor, e.ClipRectangle);

            //        RectangleF dstRect = new RectangleF(0, 0, e.ClipRectangle.Width, e.ClipRectangle.Height);

            //        using (BufferedGraphics buffGraphics = BufferedGraphicsManager.Current.Allocate(e.Graphics, e.ClipRectangle))
            //        {
            //            ClipRect = e.ClipRectangle;

            //            Graphics g = buffGraphics.Graphics;
            //            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
            //            g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
            //            g.DrawRectangle(penBorder, e.ClipRectangle);
            //            g.FillRectangle(brushBgColor, e.ClipRectangle);

            //            if (_image != null)
            //            {
            //                lock (_image)
            //                {
            //                    RectangleF dst = GetRectangle(e.ClipRectangle.Width, e.ClipRectangle.Height);
            //                    g.DrawImage(_image, dst, new RectangleF(0, 0, _image.Width, _image.Height), GraphicsUnit.Pixel);
            //                    dstRect = dst;
            //                }
            //            }
            //            rectImg = dstRect;
            //            if (_mouseSelecting|| _showSelected)
            //            {
            //                if (_selectionSequareRect) drawSelectSequare(g);
            //                else drawSelectRect(g);
            //            }

            //if (_reSizeingSelected)
            //{
            //                drawonEdge(_pointMouseMove.X, _pointMouseMove.Y, g);
            //            }

            //            if (OnDrawOnBufferedGraphics != null) OnDrawOnBufferedGraphics(g, _alpha, dstRect);

            //            //g.DrawString(Text, Font, new SolidBrush(ForeColor), TextPos);

            //            buffGraphics.Render(e.Graphics);
            //            g.Dispose();
            //        }
            //        if (OnDrawOnPaintGraphics != null) OnDrawOnPaintGraphics(e.Graphics, _alpha, e.ClipRectangle);
        }

        private void drawSelectSequare(Graphics g)
        {
            float x1 = msPt1.X * _alpha + rectImg.X;
            float y1 = msPt1.Y * _alpha + rectImg.Y;
            float x2 = msPt2.X * _alpha + rectImg.X;
            float y2 = msPt2.Y * _alpha + rectImg.Y;

            float absx = Math.Abs(x1 - x2);
            float absy = Math.Abs(y1 - y2);
            float absmax = Math.Max(absx, absy);

            g.DrawLine(Pens.Green, x1 - absmax, y1 - absmax, x1 + absmax, y1 - absmax);//特征框
            g.DrawLine(Pens.Green, x1 - absmax, y1 - absmax, x1 - absmax, y1 + absmax);
            g.DrawLine(Pens.Green, x1 + absmax, y1 + absmax, x1 - absmax, y1 + absmax);
            g.DrawLine(Pens.Green, x1 + absmax, y1 + absmax, x1 + absmax, y1 - absmax);

            g.DrawLine(Pens.Green, x1 - 5, y1, x1 + 5, y1);//十字线
            g.DrawLine(Pens.Green, x1, y1 - 5, x1, y1 + 5);
        }

        private void drawSelectRect(Graphics g)
        {
            float x1 = msPt1.X * _alpha + rectImg.X;
            float y1 = msPt1.Y * _alpha + rectImg.Y;
            float x2 = msPt2.X * _alpha + rectImg.X;
            float y2 = msPt2.Y * _alpha + rectImg.Y;
            g.DrawLine(Pens.Green, x1, y1, x2, y1);
            g.DrawLine(Pens.Green, x2, y1, x2, y2);
            g.DrawLine(Pens.Green, x2, y2, x1, y2);
            g.DrawLine(Pens.Green, x1, y2, x1, y1);


        }


        #region Calc
        private RectangleF GetRectangle(int wW, int wH)
        {
            if (CanZoom)
            {
                RectangleF dst = new RectangleF(pt0.X + ptOffset.X, pt0.Y + ptOffset.Y, _image.Width * _alpha, _image.Height * _alpha);
                return dst;
            }
            else
            {
                ptOffset = new Point(0, 0);
                _alpha = Math.Min(1.0f * Width / _image.Width, 1.0f * Height / _image.Height);
                pt0 = calcCenterLT(ClipRect, _image.Width, _image.Height, _alpha);


                float x = 0.5f * (Width - _image.Width * _alpha);
                float y = 0.5f * (Height - _image.Height * _alpha);
                RectangleF dst = new RectangleF(x, y, _image.Width * _alpha, _image.Height * _alpha);
                return dst;
            }
        }

        PointF pt0 = new PointF(0, 0);
        Point ptOffset = new Point(0, 0);
        Point ptPressed = new Point(0, 0);

        PointF calcCenterLT(Rectangle cliprect, int IW, int IH, float alpha)
        {
            int wW = cliprect.Width;
            int wH = cliprect.Height;
            float x = 0.5f * (wW - IW * alpha);
            float y = 0.5f * (wH - IH * alpha) + cliprect.Y;

            return new PointF(x, y);
        }

        PointF calcWheelOriginal(Point ptS, PointF ptO, float a1, float a2)
        {
            float x = ptS.X - (ptS.X - ptO.X) / a1 * a2;
            float y = ptS.Y - (ptS.Y - ptO.Y) / a1 * a2;
            return new PointF(x, y);
        }

        #endregion Calc

        #region Mouse Actions

        private bool _reSizeingSelected = false;
        public bool ResizeingSelected => _reSizeingSelected;

        public PointF ptUp = new PointF();

        private bool _showSelected = true;
        public bool ShowSelected => _showSelected;

        private bool _mouseSelecting = false;
        public bool MouseSelecting
        {
            get { return _mouseSelecting; }
            set
            {
                _mouseSelecting = value;
                if (_mouseSelecting)
                {
                    msPt1 = new PointF();
                    msPt2 = new PointF();
                }
                Invalidate();
            }
        }


        private bool _selectionSequareRect = false;
        public bool SelectSequareOrRect
        {
            get { return _selectionSequareRect; }
            set
            {
                _selectionSequareRect = value;
                msPt1 = new PointF();
                msPt2 = new PointF();
                Invalidate();
            }
        }

        private PointF msPt1 = new PointF();
        private PointF msPt2 = new PointF();
        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (_image == null)
            {
                base.OnMouseDown(e);
                return;
            }

            if (_reSizeingSelected)
            {
                if (e.Button == MouseButtons.Left)
                {
                    Capture = true;
                    Cursor = Cursors.SizeNS;
                }
            }
            else if (CanZoom && _image != null)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    ptPressed = e.Location;
                    Capture = true;
                }
            }

            if (_mouseSelecting && _image != null)
            {
                if (e.Button == MouseButtons.Right)
                {
                    msPt1.X = (e.Location.X - rectImg.X) / _alpha;
                    msPt1.Y = (e.Location.Y - rectImg.Y) / _alpha;
                    //Capture = true;
                }
            }

            base.OnMouseDown(e);
        }
        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (_image == null)
            {
                base.OnMouseUp(e); return;
            }

            if (_reSizeingSelected)
            {
                _reSizeingSelected = false;
                Cursor = Cursors.Arrow;
                Capture = false;
            }

            else if (CanZoom && _image != null)
            {
                if (Capture && e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    ptOffset.X = e.Location.X - ptPressed.X;
                    ptOffset.Y = e.Location.Y - ptPressed.Y;
                    pt0.X += ptOffset.X;
                    pt0.Y += ptOffset.Y;
                    ptOffset.X = ptOffset.Y = 0;
                    Capture = false;
                    Refresh();
                }
            }

            if (_mouseSelecting && _image != null)
            {
                if (e.Button == MouseButtons.Right)
                {
                    if (OnSelectRect != null) OnSelectRect(this, msPt1, msPt2);
                    Invalidate();
                    _reSizeingSelected = true;
                }
            }
            base.OnMouseUp(e);
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            if (_image == null)
            {
                base.OnMouseClick(e);
                return;
            }

            if (e.Button == MouseButtons.Left)
            {
                PointF ptclick = new PointF();
                ptclick.X = (e.Location.X - pt0.X) / _alpha;
                ptclick.Y = (e.Location.Y - pt0.Y) / _alpha;
                if (EMouseClicked != null) EMouseClicked(this, ptclick, _alpha, rectImg);
            }

            if (e.Button == MouseButtons.Right)
            {
                ptUp.X = (e.Location.X - pt0.X) / _alpha;
                ptUp.Y = (e.Location.Y - pt0.Y) / _alpha;

                if (OnMouseRightClicked != null) OnMouseRightClicked(this, null);
            }
            base.OnMouseClick(e);
        }

        private int drawonEdge(float cx, float cy, Graphics g)
        {
            cx = cx * _alpha + rectImg.X;
            cy = cy * _alpha + rectImg.Y;
            float l = Math.Min(msPt1.X, msPt2.X) * _alpha + rectImg.X;
            float t = Math.Min(msPt1.Y, msPt2.Y) * _alpha + rectImg.Y;
            float r = Math.Max(msPt1.X, msPt2.X) * _alpha + rectImg.X;
            float b = Math.Max(msPt1.Y, msPt2.Y) * _alpha + rectImg.Y;
            float dis = 5;
            float dis2 = dis + dis;
            RectangleF rtTL = new RectangleF(l - dis, t - dis, dis2, dis2);
            RectangleF rtTR = new RectangleF(r - dis, t - dis, dis2, dis2);
            RectangleF rtBR = new RectangleF(r - dis, b - dis, dis2, dis2);
            RectangleF rtBL = new RectangleF(l - dis, b - dis, dis2, dis2);

            RectangleF rtLeft = new RectangleF(l - dis, (t + b) * 0.5F - dis, dis2, dis2);
            RectangleF rtRight = new RectangleF(r - dis, (t + b) * 0.5F - dis, dis2, dis2);
            RectangleF rtTop = new RectangleF((l + r) * 0.5F, t - dis, dis2, dis2);
            RectangleF rtBottom = new RectangleF((l + r) * 0.5F, b - dis, dis2, dis2);

            Pen pr2 = new Pen(Color.Red, 2);

            if (rtTL.Contains(cx, cy))
            {
                if (g != null)
                {
                    g.DrawRectangle(pr2, rtTL.X, rtTL.Y, rtTL.Width, rtTL.Height);
                }
                return 1;
            }
            else if (rtTR.Contains(cx, cy))
            {
                if (g != null)
                {
                    g.DrawRectangle(pr2, rtTR.X, rtTR.Y, rtTR.Width, rtTR.Height);
                }
                return 2;
            }
            else if (rtBR.Contains(cx, cy))
            {
                if (g != null)
                {
                    g.DrawRectangle(pr2, rtBR.X, rtBR.Y, rtBR.Width, rtBR.Height);
                }
                return 3;
            }
            else if (rtBL.Contains(cx, cy))
            {
                if (g != null)
                {
                    g.DrawRectangle(pr2, rtBL.X, rtBL.Y, rtBL.Width, rtBL.Height);
                }
                return 4;
            }
            else if (cy > t && cy < b)
            {
                if (cx > (l - dis) && cx < (l + dis))
                {
                    if (g != null)
                    {
                        g.DrawRectangle(pr2, rtLeft.X, rtLeft.Y, rtLeft.Width, rtLeft.Height);
                    }
                    return 5;//left edge
                }
                else if (cx > (r - dis) && cx < (r + dis))
                {
                    if (g != null)
                    {
                        g.DrawRectangle(pr2, rtRight.X, rtRight.Y, rtRight.Width, rtRight.Height);
                    }
                    return 6;//right edge
                }
            }
            else if (cx > l && cy < r)
            {
                if (cy > (t - dis) && cy < (t + dis))
                {
                    if (g != null)
                    {
                        g.DrawRectangle(pr2, rtTop.X, rtTop.Y, rtTop.Width, rtTop.Height);
                    }
                    return 7;//top
                }
                else if (cy > (b - dis) && cy < (b + dis))
                {
                    if (g != null)
                    {
                        g.DrawRectangle(pr2, rtBottom.X, rtBottom.Y, rtBottom.Width, rtBottom.Height);
                    }
                    return 8;//bottom;
                }
            }
            return 0;

        }
        private PointF _pointMouseMove = new PointF();
        public PointF PointMouseMove => _pointMouseMove;

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (drawonEdge((e.Location.X - pt0.X) / _alpha, (e.Location.Y - pt0.Y) / _alpha, null) > 0)
            {
                Invalidate();
            }
            else
            if (CanZoom && _image != null)
            {
                if (_image != null && Capture)
                {
                    if (e.Button == System.Windows.Forms.MouseButtons.Left)
                    {
                        ptOffset.X = e.Location.X - ptPressed.X;
                        ptOffset.Y = e.Location.Y - ptPressed.Y;
                        Refresh();
                    }
                }
            }

            if (_mouseSelecting && _image != null)
            {
                if (e.Button == MouseButtons.Right)
                {
                    msPt2.X = (e.Location.X - rectImg.X) / _alpha;
                    msPt2.Y = (e.Location.Y - rectImg.Y) / _alpha;
                    Invalidate();
                }
            }

            if (_image != null)
            {
                _pointMouseMove.X = (e.Location.X - pt0.X) / _alpha;
                _pointMouseMove.Y = (e.Location.Y - pt0.Y) / _alpha;
                if (EMouseMove != null) EMouseMove(this, _pointMouseMove, _alpha, rectImg);
            }
            base.OnMouseMove(e);
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            if (CanZoom && _image != null)
            {
                if (_image == null) return;

                float a1 = _alpha;

                if (e.Delta > 0)
                {
                    _alpha += 0.05f;
                }
                else if (e.Delta < 0)
                {
                    if (_alpha > 0.10f)
                        _alpha -= 0.05f;
                }

                pt0 = calcWheelOriginal(e.Location, pt0, a1, _alpha);
                Refresh();
            }
            base.OnMouseWheel(e);
        }

        public void ImageCenter()
        {
            if (CanZoom && _image != null)
            {
                if (_image != null)
                {
                    ptOffset = new Point(0, 0);
                    _alpha = Math.Min(1.0f * Width / _image.Width, 1.0f * Height / _image.Height);
                    pt0 = calcCenterLT(ClipRect, _image.Width, _image.Height, _alpha);
                    Refresh();
                }
            }
        }

        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            if (CanZoom && _image != null)
            {
                if (_image != null)
                {
                    ptOffset = new Point(0, 0);
                    _alpha = Math.Min(1.0f * Width / _image.Width, 1.0f * Height / _image.Height);
                    pt0 = calcCenterLT(ClipRect, _image.Width, _image.Height, _alpha);
                    Refresh();
                }
            }
            base.OnMouseDoubleClick(e);
        }

        #endregion Mouse Actions
    }
}
